@olane/o-approval 0.8.3 → 0.8.4

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/README.md CHANGED
@@ -20,7 +20,36 @@ The `o-approval` package provides a configurable approval system that allows hum
20
20
  This package is typically included as part of the Olane OS common tools and is automatically registered on all nodes.
21
21
 
22
22
  ```bash
23
- npm install @olane/o-approval
23
+ pnpm install @olane/o-approval
24
+ ```
25
+
26
+ ## Dependencies
27
+
28
+ This tool requires the following nodes to be running on the network:
29
+
30
+ - **`o://leader`** - Used for persistent storage of approval preferences (whitelist/blacklist). Preferences are saved so that "always allow" and "never allow" choices survive restarts.
31
+ - **`o://human`** - Used to prompt a human agent for approval decisions. When an action requires review, the approval tool sends a question to the human node and waits for a response.
32
+
33
+ ## Response Structure
34
+
35
+ When calling the approval tool via `node.use()`, responses follow the standard Olane response structure:
36
+
37
+ ```typescript
38
+ const response = await node.use(new oNodeAddress('o://approval'), {
39
+ method: 'request_approval',
40
+ params: { ... }
41
+ });
42
+
43
+ // response.result.success - boolean indicating success or failure
44
+ // response.result.data - the return value on success
45
+ // response.result.error - error message on failure
46
+
47
+ if (response.result.success) {
48
+ const data = response.result.data;
49
+ console.log('Approved:', data.approved);
50
+ } else {
51
+ console.error('Error:', response.result.error);
52
+ }
24
53
  ```
25
54
 
26
55
  ## Usage
@@ -79,9 +108,9 @@ await approvalTool.start();
79
108
  #### Request Approval
80
109
 
81
110
  ```typescript
82
- import { oAddress } from '@olane/o-core';
111
+ import { oNodeAddress } from '@olane/o-node';
83
112
 
84
- const response = await node.use(new oAddress('o://approval'), {
113
+ const response = await node.use(new oNodeAddress('o://approval'), {
85
114
  method: 'request_approval',
86
115
  params: {
87
116
  toolAddress: 'o://storage',
@@ -1,4 +1,3 @@
1
- import { ToolResult } from '@olane/o-tool';
2
1
  import { oRequest } from '@olane/o-core';
3
2
  import { oApprovalConfig } from './interfaces/approval-config.js';
4
3
  import { oLaneTool } from '@olane/o-lane';
@@ -13,7 +12,7 @@ export declare class oApprovalTool extends oLaneTool {
13
12
  /**
14
13
  * Request human approval for an action
15
14
  */
16
- _tool_request_approval(request: oRequest): Promise<ToolResult>;
15
+ _tool_request_approval(request: oRequest): Promise<any>;
17
16
  /**
18
17
  * Format the approval prompt for the human
19
18
  */
@@ -37,14 +36,14 @@ export declare class oApprovalTool extends oLaneTool {
37
36
  /**
38
37
  * Set an approval preference manually
39
38
  */
40
- _tool_set_preference(request: oRequest): Promise<ToolResult>;
39
+ _tool_set_preference(request: oRequest): Promise<any>;
41
40
  /**
42
41
  * Get the current approval mode
43
42
  */
44
- _tool_get_mode(request: oRequest): Promise<ToolResult>;
43
+ _tool_get_mode(request: oRequest): Promise<any>;
45
44
  /**
46
45
  * Set the approval mode
47
46
  */
48
- _tool_set_mode(request: oRequest): Promise<ToolResult>;
47
+ _tool_set_mode(request: oRequest): Promise<any>;
49
48
  }
50
49
  //# sourceMappingURL=o-approval.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"o-approval.tool.d.ts","sourceRoot":"","sources":["../../src/o-approval.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAY,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EACL,eAAe,EAGhB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK1C,qBAAa,aAAc,SAAQ,SAAS;IAC1C,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,WAAW,CAAsB;gBAE7B,MAAM,EAAE,eAAe;IAenC;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAuHpE;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmB5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;YACW,cAAc;IAU5B;;OAEG;YACW,cAAc;IAU5B;;OAEG;YACW,eAAe;IAY7B;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IA+BlE;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAQ5D;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CA+B7D"}
1
+ {"version":3,"file":"o-approval.tool.d.ts","sourceRoot":"","sources":["../../src/o-approval.tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EACL,eAAe,EAGhB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAK1C,qBAAa,aAAc,SAAQ,SAAS;IAC1C,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,WAAW,CAAsB;gBAE7B,MAAM,EAAE,eAAe;IAenC;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACG,sBAAsB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAyF7D;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAmB5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkB7B;;OAEG;YACW,cAAc;IAU5B;;OAEG;YACW,cAAc;IAU5B;;OAEG;YACW,eAAe;IAY7B;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAmB3D;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAOrD;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;CAoBtD"}
@@ -49,101 +49,72 @@ export class oApprovalTool extends oLaneTool {
49
49
  * Request human approval for an action
50
50
  */
51
51
  async _tool_request_approval(request) {
52
- try {
53
- const toolAddress = request.params.toolAddress;
54
- const method = request.params.method;
55
- const params = request.params.params;
56
- const intent = request.params.intent;
57
- const approvalRequest = {
58
- toolAddress,
59
- method,
60
- params,
61
- intent,
62
- timestamp: Date.now(),
52
+ const toolAddress = request.params.toolAddress;
53
+ const method = request.params.method;
54
+ const params = request.params.params;
55
+ const intent = request.params.intent;
56
+ const approvalRequest = {
57
+ toolAddress,
58
+ method,
59
+ params,
60
+ intent,
61
+ timestamp: Date.now(),
62
+ };
63
+ const toolMethod = `${toolAddress}/${method}`;
64
+ // Check if this action is blacklisted
65
+ if (this.preferences.blacklist?.includes(toolMethod)) {
66
+ this.logger.warn(`Action denied by blacklist: ${toolMethod}`);
67
+ throw new Error('Action denied by approval blacklist');
68
+ }
69
+ // Check if this action is whitelisted
70
+ if (this.preferences.whitelist?.includes(toolMethod)) {
71
+ this.logger.info(`Action pre-approved by whitelist: ${toolMethod}`);
72
+ return {
73
+ approved: true,
74
+ decision: 'approve',
63
75
  };
64
- const toolMethod = `${toolAddress}/${method}`;
65
- // Check if this action is blacklisted
66
- if (this.preferences.blacklist?.includes(toolMethod)) {
67
- this.logger.warn(`Action denied by blacklist: ${toolMethod}`);
68
- return {
69
- success: false,
70
- error: 'Action denied by approval blacklist',
71
- approved: false,
72
- };
73
- }
74
- // Check if this action is whitelisted
75
- if (this.preferences.whitelist?.includes(toolMethod)) {
76
- this.logger.info(`Action pre-approved by whitelist: ${toolMethod}`);
77
- return {
78
- success: true,
79
- approved: true,
80
- decision: 'approve',
81
- };
82
- }
83
- // Check if approval is needed based on mode
84
- if (!this.needsApproval(toolAddress, method)) {
85
- this.logger.debug(`Approval not required for ${toolMethod} (mode: ${this.mode})`);
86
- return {
87
- success: true,
88
- approved: true,
89
- decision: 'approve',
90
- };
91
- }
92
- // Format the approval prompt
93
- const question = this.formatApprovalPrompt(approvalRequest);
94
- // Request approval from human
95
- this.logger.info(`Requesting approval for: ${toolMethod}`);
96
- const timeout = this.preferences.timeout || DEFAULT_TIMEOUT;
97
- const approvalPromise = this.use(new oAddress('o://human'), {
98
- method: 'question',
99
- params: { question },
100
- });
101
- // Implement timeout
102
- const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Approval timeout')), timeout));
103
- try {
104
- const response = await Promise.race([approvalPromise, timeoutPromise]);
105
- const answer = response.result.data?.answer
106
- ?.trim()
107
- .toLowerCase();
108
- // Parse the response
109
- const decision = this.parseApprovalResponse(answer);
110
- // Handle preference storage
111
- if (decision === 'always') {
112
- await this.addToWhitelist(toolMethod);
113
- this.logger.info(`Added to whitelist: ${toolMethod}`);
114
- }
115
- else if (decision === 'never') {
116
- await this.addToBlacklist(toolMethod);
117
- this.logger.info(`Added to blacklist: ${toolMethod}`);
118
- }
119
- const approved = decision === 'approve' || decision === 'always';
120
- this.logger.info(`Approval decision for ${toolMethod}: ${decision} (approved: ${approved})`);
121
- return {
122
- success: true,
123
- approved,
124
- decision,
125
- timestamp: Date.now(),
126
- };
127
- }
128
- catch (error) {
129
- // Timeout or error - default to deny
130
- this.logger.error(`Approval timeout or error for ${toolMethod}:`, error.message);
131
- return {
132
- success: false,
133
- error: error.message || 'Approval timeout',
134
- approved: false,
135
- decision: 'deny',
136
- };
137
- }
138
76
  }
139
- catch (error) {
140
- this.logger.error('Error in approval request:', error);
77
+ // Check if approval is needed based on mode
78
+ if (!this.needsApproval(toolAddress, method)) {
79
+ this.logger.debug(`Approval not required for ${toolMethod} (mode: ${this.mode})`);
141
80
  return {
142
- success: false,
143
- error: error.message || 'Unknown error',
144
- approved: false,
81
+ approved: true,
82
+ decision: 'approve',
145
83
  };
146
84
  }
85
+ // Format the approval prompt
86
+ const question = this.formatApprovalPrompt(approvalRequest);
87
+ // Request approval from human
88
+ this.logger.info(`Requesting approval for: ${toolMethod}`);
89
+ const timeout = this.preferences.timeout || DEFAULT_TIMEOUT;
90
+ const approvalPromise = this.use(new oAddress('o://human'), {
91
+ method: 'question',
92
+ params: { question },
93
+ });
94
+ // Implement timeout
95
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Approval timeout')), timeout));
96
+ const response = await Promise.race([approvalPromise, timeoutPromise]);
97
+ const answer = response.result.data?.answer
98
+ ?.trim()
99
+ .toLowerCase();
100
+ // Parse the response
101
+ const decision = this.parseApprovalResponse(answer);
102
+ // Handle preference storage
103
+ if (decision === 'always') {
104
+ await this.addToWhitelist(toolMethod);
105
+ this.logger.info(`Added to whitelist: ${toolMethod}`);
106
+ }
107
+ else if (decision === 'never') {
108
+ await this.addToBlacklist(toolMethod);
109
+ this.logger.info(`Added to blacklist: ${toolMethod}`);
110
+ }
111
+ const approved = decision === 'approve' || decision === 'always';
112
+ this.logger.info(`Approval decision for ${toolMethod}: ${decision} (approved: ${approved})`);
113
+ return {
114
+ approved,
115
+ decision,
116
+ timestamp: Date.now(),
117
+ };
147
118
  }
148
119
  /**
149
120
  * Format the approval prompt for the human
@@ -229,43 +200,29 @@ Your response:`.trim();
229
200
  * Set an approval preference manually
230
201
  */
231
202
  async _tool_set_preference(request) {
232
- try {
233
- const toolMethod = request.params.toolMethod;
234
- const preference = request.params.preference;
235
- if (preference === 'allow') {
236
- await this.addToWhitelist(toolMethod);
237
- return {
238
- success: true,
239
- message: `Added ${toolMethod} to whitelist`,
240
- };
241
- }
242
- else if (preference === 'deny') {
243
- await this.addToBlacklist(toolMethod);
244
- return {
245
- success: true,
246
- message: `Added ${toolMethod} to blacklist`,
247
- };
248
- }
249
- else {
250
- return {
251
- success: false,
252
- error: 'Invalid preference. Use "allow" or "deny"',
253
- };
254
- }
203
+ const toolMethod = request.params.toolMethod;
204
+ const preference = request.params.preference;
205
+ if (preference === 'allow') {
206
+ await this.addToWhitelist(toolMethod);
207
+ return {
208
+ message: `Added ${toolMethod} to whitelist`,
209
+ };
255
210
  }
256
- catch (error) {
211
+ else if (preference === 'deny') {
212
+ await this.addToBlacklist(toolMethod);
257
213
  return {
258
- success: false,
259
- error: error.message || 'Unknown error',
214
+ message: `Added ${toolMethod} to blacklist`,
260
215
  };
261
216
  }
217
+ else {
218
+ throw new Error('Invalid preference. Use "allow" or "deny"');
219
+ }
262
220
  }
263
221
  /**
264
222
  * Get the current approval mode
265
223
  */
266
224
  async _tool_get_mode(request) {
267
225
  return {
268
- success: true,
269
226
  mode: this.mode,
270
227
  preferences: this.preferences,
271
228
  };
@@ -274,31 +231,19 @@ Your response:`.trim();
274
231
  * Set the approval mode
275
232
  */
276
233
  async _tool_set_mode(request) {
277
- try {
278
- const mode = request.params.mode;
279
- if (!['allow', 'review', 'auto'].includes(mode)) {
280
- return {
281
- success: false,
282
- error: 'Invalid mode. Use "allow", "review", or "auto"',
283
- };
284
- }
285
- this.mode = mode;
286
- this.logger.info(`Approval mode set to: ${this.mode}`);
287
- // Save mode to OS config
288
- await this.use(new oAddress('o://leader'), {
289
- method: 'update_approval_mode',
290
- params: { mode: this.mode },
291
- });
292
- return {
293
- success: true,
294
- mode: this.mode,
295
- };
296
- }
297
- catch (error) {
298
- return {
299
- success: false,
300
- error: error.message || 'Unknown error',
301
- };
302
- }
234
+ const mode = request.params.mode;
235
+ if (!['allow', 'review', 'auto'].includes(mode)) {
236
+ throw new Error('Invalid mode. Use "allow", "review", or "auto"');
237
+ }
238
+ this.mode = mode;
239
+ this.logger.info(`Approval mode set to: ${this.mode}`);
240
+ // Save mode to OS config
241
+ await this.use(new oAddress('o://leader'), {
242
+ method: 'update_approval_mode',
243
+ params: { mode: this.mode },
244
+ });
245
+ return {
246
+ mode: this.mode,
247
+ };
303
248
  }
304
249
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/o-approval",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -53,13 +53,13 @@
53
53
  "typescript": "5.4.5"
54
54
  },
55
55
  "dependencies": {
56
- "@olane/o-config": "0.8.3",
57
- "@olane/o-core": "0.8.3",
58
- "@olane/o-lane": "0.8.3",
59
- "@olane/o-protocol": "0.8.3",
60
- "@olane/o-tool": "0.8.3",
56
+ "@olane/o-config": "0.8.4",
57
+ "@olane/o-core": "0.8.4",
58
+ "@olane/o-lane": "0.8.4",
59
+ "@olane/o-protocol": "0.8.4",
60
+ "@olane/o-tool": "0.8.4",
61
61
  "debug": "^4.4.1",
62
62
  "dotenv": "^16.5.0"
63
63
  },
64
- "gitHead": "189c0cf7b6dd9d5d961f5424af21d37978092d9e"
64
+ "gitHead": "b53623b1ad4365133911722f80d5597a72b65bf2"
65
65
  }