@lelu-auth/lelu 0.1.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.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/client-BD9h8CBT.d.mts +235 -0
- package/dist/client-BD9h8CBT.d.ts +235 -0
- package/dist/express/index.d.mts +31 -0
- package/dist/express/index.d.ts +31 -0
- package/dist/express/index.js +277 -0
- package/dist/express/index.js.map +1 -0
- package/dist/express/index.mjs +275 -0
- package/dist/express/index.mjs.map +1 -0
- package/dist/index.d.mts +104 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.js +306 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +296 -0
- package/dist/index.mjs.map +1 -0
- package/dist/langchain/index.d.mts +110 -0
- package/dist/langchain/index.d.ts +110 -0
- package/dist/langchain/index.js +159 -0
- package/dist/langchain/index.js.map +1 -0
- package/dist/langchain/index.mjs +156 -0
- package/dist/langchain/index.mjs.map +1 -0
- package/dist/react/index.d.mts +52 -0
- package/dist/react/index.d.ts +52 -0
- package/dist/react/index.js +670 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +666 -0
- package/dist/react/index.mjs.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var openai = require('openai');
|
|
4
|
+
|
|
5
|
+
// src/langchain/secure-tool.ts
|
|
6
|
+
var SecureTool = class {
|
|
7
|
+
name;
|
|
8
|
+
description;
|
|
9
|
+
opts;
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
this.opts = opts;
|
|
12
|
+
this.name = opts.name;
|
|
13
|
+
this.description = opts.description;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* invoke — LangChain StructuredTool / DynamicTool compatible entry point.
|
|
17
|
+
*
|
|
18
|
+
* 1. Calls Lelu `agentAuthorize` with the confidence score
|
|
19
|
+
* 2a. Allowed → runs the wrapped tool function
|
|
20
|
+
* 2b. Requires human review → returns structured "pending" message
|
|
21
|
+
* 2c. Denied → returns structured refusal string (LLM can self-correct)
|
|
22
|
+
*/
|
|
23
|
+
async invoke(input) {
|
|
24
|
+
const result = await this._check(input);
|
|
25
|
+
if (result.allowed) {
|
|
26
|
+
return result.output;
|
|
27
|
+
}
|
|
28
|
+
if (this.opts.throwOnDeny) {
|
|
29
|
+
throw new Error(`[Lelu] Tool "${this.name}" denied: ${result.reason}`);
|
|
30
|
+
}
|
|
31
|
+
return result.output;
|
|
32
|
+
}
|
|
33
|
+
/** call — alias for older LangChain versions. */
|
|
34
|
+
async call(input) {
|
|
35
|
+
return this.invoke(input);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check authorization and run the tool if permitted.
|
|
39
|
+
* Returns a full ToolCallResult for programmatic consumers.
|
|
40
|
+
*/
|
|
41
|
+
async checkAndCall(input) {
|
|
42
|
+
return this._check(input);
|
|
43
|
+
}
|
|
44
|
+
async _check(input) {
|
|
45
|
+
const { actor, requiredPermission, client, func, throwOnDeny } = this.opts;
|
|
46
|
+
const confidence = this.opts.confidence ?? 1;
|
|
47
|
+
const actingFor = this.opts.actingFor ?? "";
|
|
48
|
+
let decision;
|
|
49
|
+
try {
|
|
50
|
+
decision = await client.agentAuthorize({
|
|
51
|
+
actor,
|
|
52
|
+
action: requiredPermission,
|
|
53
|
+
context: {
|
|
54
|
+
confidence,
|
|
55
|
+
actingFor
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const msg = `[Lelu] Authorization check failed for "${this.name}": ${String(err)}`;
|
|
60
|
+
if (throwOnDeny) throw new Error(msg);
|
|
61
|
+
return {
|
|
62
|
+
allowed: false,
|
|
63
|
+
output: msg,
|
|
64
|
+
requiresHumanReview: false,
|
|
65
|
+
reason: String(err)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (decision.requiresHumanReview) {
|
|
69
|
+
const msg = `[Lelu] Action "${this.name}" is queued for human review. Reason: ${decision.reason}. Please wait for approval before proceeding.`;
|
|
70
|
+
return {
|
|
71
|
+
allowed: false,
|
|
72
|
+
output: msg,
|
|
73
|
+
requiresHumanReview: true,
|
|
74
|
+
reason: decision.reason
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (!decision.allowed) {
|
|
78
|
+
const msg = `[Lelu] Action "${this.name}" was denied. Reason: ${decision.reason}. Downgraded scope: ${decision.downgradedScope ?? "none"}.`;
|
|
79
|
+
return {
|
|
80
|
+
allowed: false,
|
|
81
|
+
output: msg,
|
|
82
|
+
requiresHumanReview: false,
|
|
83
|
+
reason: decision.reason
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const output = await func(input);
|
|
87
|
+
return {
|
|
88
|
+
allowed: true,
|
|
89
|
+
output,
|
|
90
|
+
requiresHumanReview: false,
|
|
91
|
+
reason: decision.reason
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var SemanticPolicyGenerator = class {
|
|
96
|
+
openai;
|
|
97
|
+
constructor(apiKey) {
|
|
98
|
+
this.openai = new openai.OpenAI({ apiKey });
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Converts a natural language description into a deterministic Rego policy.
|
|
102
|
+
*
|
|
103
|
+
* @param description Natural language description of the policy (e.g., "Don't let the bot refund more than $50 unless approved by a finance manager.")
|
|
104
|
+
* @param packageName The Rego package name (default: "lelu.authz")
|
|
105
|
+
* @returns The generated Rego policy string
|
|
106
|
+
*/
|
|
107
|
+
async generateRegoPolicy(description, packageName = "lelu.authz") {
|
|
108
|
+
const prompt = `
|
|
109
|
+
You are an expert in Open Policy Agent (OPA) Rego policies.
|
|
110
|
+
Your task is to convert the following natural language description into a valid, deterministic Rego policy for the Lelu Auth Permission Engine.
|
|
111
|
+
|
|
112
|
+
The policy must output an object with the following structure:
|
|
113
|
+
{
|
|
114
|
+
"allowed": bool,
|
|
115
|
+
"reason": string,
|
|
116
|
+
"downgraded_scope": string (optional),
|
|
117
|
+
"requires_human_review": bool (optional)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
The input to the policy will be an object with the following structure:
|
|
121
|
+
{
|
|
122
|
+
"kind": "agent" | "human",
|
|
123
|
+
"actor": string,
|
|
124
|
+
"action": string,
|
|
125
|
+
"resource": map[string]any,
|
|
126
|
+
"confidence": number (0.0 to 1.0),
|
|
127
|
+
"acting_for": string,
|
|
128
|
+
"scope": string
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
Natural Language Description:
|
|
132
|
+
"${description}"
|
|
133
|
+
|
|
134
|
+
Package Name:
|
|
135
|
+
package ${packageName}
|
|
136
|
+
|
|
137
|
+
Requirements:
|
|
138
|
+
1. Output ONLY valid Rego code.
|
|
139
|
+
2. Do not include markdown formatting (like \`\`\`rego).
|
|
140
|
+
3. Ensure the default state is deny (default allowed = false).
|
|
141
|
+
4. Include comments explaining the logic.
|
|
142
|
+
`;
|
|
143
|
+
const response = await this.openai.chat.completions.create({
|
|
144
|
+
model: "gpt-4-turbo-preview",
|
|
145
|
+
messages: [
|
|
146
|
+
{ role: "system", content: "You are a Rego policy generation assistant. Output only raw Rego code." },
|
|
147
|
+
{ role: "user", content: prompt }
|
|
148
|
+
],
|
|
149
|
+
temperature: 0.1
|
|
150
|
+
});
|
|
151
|
+
const regoCode = response.choices[0]?.message.content?.trim() || "";
|
|
152
|
+
return regoCode.replace(/^```rego\n/, "").replace(/\n```$/, "");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
exports.SecureTool = SecureTool;
|
|
157
|
+
exports.SemanticPolicyGenerator = SemanticPolicyGenerator;
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
159
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/langchain/secure-tool.ts","../../src/langchain/SemanticPolicyGenerator.ts"],"names":["OpenAI"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * SecureTool — LangChain tool wrapper with Confidence-Aware Auth\n *\n * Intercepts every tool call and gates it through the Lelu engine before\n * execution. Returns a structured refusal string when denied so the LLM can\n * self-correct, queue for human review, or escalate.\n *\n * Usage:\n * ```typescript\n * import { SecureTool } from 'lelu/langchain';\n *\n * const refundTool = new SecureTool({\n * name: 'process_refund',\n * description: 'Processes a customer refund',\n * actor: 'invoice_bot',\n * requiredPermission: 'invoice:refund',\n * client: leluClient,\n * func: async (input) => {\n * // your tool implementation\n * return `Refund processed for ${input}`;\n * },\n * });\n * ```\n */\n\nimport { LeluClient } from \"../client.js\";\n\n// ─── Minimal LangChain-compatible interface ───────────────────────────────────\n\n/** Minimal interface a wrapped tool must expose. */\nexport interface ToolLike {\n name: string;\n description: string;\n /** LangChain StructuredTool / DynamicTool use `invoke`. */\n invoke?: (input: string, ...args: unknown[]) => Promise<string>;\n /** Older LangChain versions use `call`. */\n call?: (input: string, ...args: unknown[]) => Promise<string>;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\nexport interface SecureToolOptions {\n /** Unique tool name (used as the action in authz request). */\n name: string;\n /** Human-readable description forwarded to the LLM. */\n description: string;\n /** The Lelu agent scope / actor name. */\n actor: string;\n /** The permission string being checked (e.g. \"invoice:refund\"). */\n requiredPermission: string;\n /** Configured LeluClient. */\n client: LeluClient;\n /** The actual tool function to execute when authorized. */\n func: (input: string) => Promise<string>;\n /**\n * Optional: LLM confidence score for this invocation (0.0–1.0).\n * If omitted, defaults to 1.0 (full confidence assumed).\n */\n confidence?: number;\n /**\n * Optional: the human user the agent is acting on behalf of.\n */\n actingFor?: string;\n /**\n * Optional: if true, throw an error when denied instead of returning\n * a structured refusal string. Default: false (silent fail, structured msg).\n */\n throwOnDeny?: boolean;\n}\n\n// ─── Result ───────────────────────────────────────────────────────────────────\n\nexport interface ToolCallResult {\n allowed: boolean;\n output: string;\n requiresHumanReview: boolean;\n reviewId?: string;\n reason: string;\n}\n\n// ─── SecureTool ───────────────────────────────────────────────────────────────\n\n/**\n * SecureTool wraps a tool function with Lelu's Confidence-Aware Auth gate.\n *\n * Implements the LangChain Tool interface (name + description + invoke)\n * so it can be dropped into any LangChain agent tool array:\n * ```typescript\n * const agent = await createOpenAIFunctionsAgent({ tools: [refundTool] });\n * ```\n */\nexport class SecureTool {\n readonly name: string;\n readonly description: string;\n\n private readonly opts: SecureToolOptions;\n\n constructor(opts: SecureToolOptions) {\n this.opts = opts;\n this.name = opts.name;\n this.description = opts.description;\n }\n\n /**\n * invoke — LangChain StructuredTool / DynamicTool compatible entry point.\n *\n * 1. Calls Lelu `agentAuthorize` with the confidence score\n * 2a. Allowed → runs the wrapped tool function\n * 2b. Requires human review → returns structured \"pending\" message\n * 2c. Denied → returns structured refusal string (LLM can self-correct)\n */\n async invoke(input: string): Promise<string> {\n const result = await this._check(input);\n if (result.allowed) {\n return result.output;\n }\n if (this.opts.throwOnDeny) {\n throw new Error(`[Lelu] Tool \"${this.name}\" denied: ${result.reason}`);\n }\n return result.output; // structured refusal or pending message\n }\n\n /** call — alias for older LangChain versions. */\n async call(input: string): Promise<string> {\n return this.invoke(input);\n }\n\n /**\n * Check authorization and run the tool if permitted.\n * Returns a full ToolCallResult for programmatic consumers.\n */\n async checkAndCall(input: string): Promise<ToolCallResult> {\n return this._check(input);\n }\n\n private async _check(input: string): Promise<ToolCallResult> {\n const { actor, requiredPermission, client, func, throwOnDeny } = this.opts;\n const confidence = this.opts.confidence ?? 1.0;\n const actingFor = this.opts.actingFor ?? \"\";\n\n let decision;\n try {\n decision = await client.agentAuthorize({\n actor,\n action: requiredPermission,\n context: {\n confidence,\n actingFor,\n },\n });\n } catch (err) {\n const msg = `[Lelu] Authorization check failed for \"${this.name}\": ${String(err)}`;\n if (throwOnDeny) throw new Error(msg);\n return {\n allowed: false,\n output: msg,\n requiresHumanReview: false,\n reason: String(err),\n };\n }\n\n // Human review required — queue and return pending message.\n if (decision.requiresHumanReview) {\n const msg =\n `[Lelu] Action \"${this.name}\" is queued for human review. ` +\n `Reason: ${decision.reason}. Please wait for approval before proceeding.`;\n return {\n allowed: false,\n output: msg,\n requiresHumanReview: true,\n reason: decision.reason,\n };\n }\n\n // Hard deny — return structured refusal.\n if (!decision.allowed) {\n const msg =\n `[Lelu] Action \"${this.name}\" was denied. ` +\n `Reason: ${decision.reason}. ` +\n `Downgraded scope: ${decision.downgradedScope ?? \"none\"}.`;\n return {\n allowed: false,\n output: msg,\n requiresHumanReview: false,\n reason: decision.reason,\n };\n }\n\n // Authorized — run the tool.\n const output = await func(input);\n return {\n allowed: true,\n output,\n requiresHumanReview: false,\n reason: decision.reason,\n };\n }\n}\n","import { OpenAI } from 'openai';\n\nexport class SemanticPolicyGenerator {\n private openai: OpenAI;\n\n constructor(apiKey: string) {\n this.openai = new OpenAI({ apiKey });\n }\n\n /**\n * Converts a natural language description into a deterministic Rego policy.\n * \n * @param description Natural language description of the policy (e.g., \"Don't let the bot refund more than $50 unless approved by a finance manager.\")\n * @param packageName The Rego package name (default: \"lelu.authz\")\n * @returns The generated Rego policy string\n */\n async generateRegoPolicy(description: string, packageName: string = \"lelu.authz\"): Promise<string> {\n const prompt = `\nYou are an expert in Open Policy Agent (OPA) Rego policies.\nYour task is to convert the following natural language description into a valid, deterministic Rego policy for the Lelu Auth Permission Engine.\n\nThe policy must output an object with the following structure:\n{\n \"allowed\": bool,\n \"reason\": string,\n \"downgraded_scope\": string (optional),\n \"requires_human_review\": bool (optional)\n}\n\nThe input to the policy will be an object with the following structure:\n{\n \"kind\": \"agent\" | \"human\",\n \"actor\": string,\n \"action\": string,\n \"resource\": map[string]any,\n \"confidence\": number (0.0 to 1.0),\n \"acting_for\": string,\n \"scope\": string\n}\n\nNatural Language Description:\n\"${description}\"\n\nPackage Name:\npackage ${packageName}\n\nRequirements:\n1. Output ONLY valid Rego code.\n2. Do not include markdown formatting (like \\`\\`\\`rego).\n3. Ensure the default state is deny (default allowed = false).\n4. Include comments explaining the logic.\n`;\n\n const response = await this.openai.chat.completions.create({\n model: \"gpt-4-turbo-preview\",\n messages: [\n { role: \"system\", content: \"You are a Rego policy generation assistant. Output only raw Rego code.\" },\n { role: \"user\", content: prompt }\n ],\n temperature: 0.1,\n });\n\n const regoCode = response.choices[0]?.message.content?.trim() || \"\";\n \n // Strip markdown code blocks if the LLM accidentally included them\n return regoCode.replace(/^```rego\\n/, '').replace(/\\n```$/, '');\n }\n}\n"]}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { OpenAI } from 'openai';
|
|
2
|
+
|
|
3
|
+
// src/langchain/secure-tool.ts
|
|
4
|
+
var SecureTool = class {
|
|
5
|
+
name;
|
|
6
|
+
description;
|
|
7
|
+
opts;
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
this.opts = opts;
|
|
10
|
+
this.name = opts.name;
|
|
11
|
+
this.description = opts.description;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* invoke — LangChain StructuredTool / DynamicTool compatible entry point.
|
|
15
|
+
*
|
|
16
|
+
* 1. Calls Lelu `agentAuthorize` with the confidence score
|
|
17
|
+
* 2a. Allowed → runs the wrapped tool function
|
|
18
|
+
* 2b. Requires human review → returns structured "pending" message
|
|
19
|
+
* 2c. Denied → returns structured refusal string (LLM can self-correct)
|
|
20
|
+
*/
|
|
21
|
+
async invoke(input) {
|
|
22
|
+
const result = await this._check(input);
|
|
23
|
+
if (result.allowed) {
|
|
24
|
+
return result.output;
|
|
25
|
+
}
|
|
26
|
+
if (this.opts.throwOnDeny) {
|
|
27
|
+
throw new Error(`[Lelu] Tool "${this.name}" denied: ${result.reason}`);
|
|
28
|
+
}
|
|
29
|
+
return result.output;
|
|
30
|
+
}
|
|
31
|
+
/** call — alias for older LangChain versions. */
|
|
32
|
+
async call(input) {
|
|
33
|
+
return this.invoke(input);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check authorization and run the tool if permitted.
|
|
37
|
+
* Returns a full ToolCallResult for programmatic consumers.
|
|
38
|
+
*/
|
|
39
|
+
async checkAndCall(input) {
|
|
40
|
+
return this._check(input);
|
|
41
|
+
}
|
|
42
|
+
async _check(input) {
|
|
43
|
+
const { actor, requiredPermission, client, func, throwOnDeny } = this.opts;
|
|
44
|
+
const confidence = this.opts.confidence ?? 1;
|
|
45
|
+
const actingFor = this.opts.actingFor ?? "";
|
|
46
|
+
let decision;
|
|
47
|
+
try {
|
|
48
|
+
decision = await client.agentAuthorize({
|
|
49
|
+
actor,
|
|
50
|
+
action: requiredPermission,
|
|
51
|
+
context: {
|
|
52
|
+
confidence,
|
|
53
|
+
actingFor
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
} catch (err) {
|
|
57
|
+
const msg = `[Lelu] Authorization check failed for "${this.name}": ${String(err)}`;
|
|
58
|
+
if (throwOnDeny) throw new Error(msg);
|
|
59
|
+
return {
|
|
60
|
+
allowed: false,
|
|
61
|
+
output: msg,
|
|
62
|
+
requiresHumanReview: false,
|
|
63
|
+
reason: String(err)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (decision.requiresHumanReview) {
|
|
67
|
+
const msg = `[Lelu] Action "${this.name}" is queued for human review. Reason: ${decision.reason}. Please wait for approval before proceeding.`;
|
|
68
|
+
return {
|
|
69
|
+
allowed: false,
|
|
70
|
+
output: msg,
|
|
71
|
+
requiresHumanReview: true,
|
|
72
|
+
reason: decision.reason
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (!decision.allowed) {
|
|
76
|
+
const msg = `[Lelu] Action "${this.name}" was denied. Reason: ${decision.reason}. Downgraded scope: ${decision.downgradedScope ?? "none"}.`;
|
|
77
|
+
return {
|
|
78
|
+
allowed: false,
|
|
79
|
+
output: msg,
|
|
80
|
+
requiresHumanReview: false,
|
|
81
|
+
reason: decision.reason
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const output = await func(input);
|
|
85
|
+
return {
|
|
86
|
+
allowed: true,
|
|
87
|
+
output,
|
|
88
|
+
requiresHumanReview: false,
|
|
89
|
+
reason: decision.reason
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var SemanticPolicyGenerator = class {
|
|
94
|
+
openai;
|
|
95
|
+
constructor(apiKey) {
|
|
96
|
+
this.openai = new OpenAI({ apiKey });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Converts a natural language description into a deterministic Rego policy.
|
|
100
|
+
*
|
|
101
|
+
* @param description Natural language description of the policy (e.g., "Don't let the bot refund more than $50 unless approved by a finance manager.")
|
|
102
|
+
* @param packageName The Rego package name (default: "lelu.authz")
|
|
103
|
+
* @returns The generated Rego policy string
|
|
104
|
+
*/
|
|
105
|
+
async generateRegoPolicy(description, packageName = "lelu.authz") {
|
|
106
|
+
const prompt = `
|
|
107
|
+
You are an expert in Open Policy Agent (OPA) Rego policies.
|
|
108
|
+
Your task is to convert the following natural language description into a valid, deterministic Rego policy for the Lelu Auth Permission Engine.
|
|
109
|
+
|
|
110
|
+
The policy must output an object with the following structure:
|
|
111
|
+
{
|
|
112
|
+
"allowed": bool,
|
|
113
|
+
"reason": string,
|
|
114
|
+
"downgraded_scope": string (optional),
|
|
115
|
+
"requires_human_review": bool (optional)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
The input to the policy will be an object with the following structure:
|
|
119
|
+
{
|
|
120
|
+
"kind": "agent" | "human",
|
|
121
|
+
"actor": string,
|
|
122
|
+
"action": string,
|
|
123
|
+
"resource": map[string]any,
|
|
124
|
+
"confidence": number (0.0 to 1.0),
|
|
125
|
+
"acting_for": string,
|
|
126
|
+
"scope": string
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Natural Language Description:
|
|
130
|
+
"${description}"
|
|
131
|
+
|
|
132
|
+
Package Name:
|
|
133
|
+
package ${packageName}
|
|
134
|
+
|
|
135
|
+
Requirements:
|
|
136
|
+
1. Output ONLY valid Rego code.
|
|
137
|
+
2. Do not include markdown formatting (like \`\`\`rego).
|
|
138
|
+
3. Ensure the default state is deny (default allowed = false).
|
|
139
|
+
4. Include comments explaining the logic.
|
|
140
|
+
`;
|
|
141
|
+
const response = await this.openai.chat.completions.create({
|
|
142
|
+
model: "gpt-4-turbo-preview",
|
|
143
|
+
messages: [
|
|
144
|
+
{ role: "system", content: "You are a Rego policy generation assistant. Output only raw Rego code." },
|
|
145
|
+
{ role: "user", content: prompt }
|
|
146
|
+
],
|
|
147
|
+
temperature: 0.1
|
|
148
|
+
});
|
|
149
|
+
const regoCode = response.choices[0]?.message.content?.trim() || "";
|
|
150
|
+
return regoCode.replace(/^```rego\n/, "").replace(/\n```$/, "");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export { SecureTool, SemanticPolicyGenerator };
|
|
155
|
+
//# sourceMappingURL=index.mjs.map
|
|
156
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/langchain/secure-tool.ts","../../src/langchain/SemanticPolicyGenerator.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * SecureTool — LangChain tool wrapper with Confidence-Aware Auth\n *\n * Intercepts every tool call and gates it through the Lelu engine before\n * execution. Returns a structured refusal string when denied so the LLM can\n * self-correct, queue for human review, or escalate.\n *\n * Usage:\n * ```typescript\n * import { SecureTool } from 'lelu/langchain';\n *\n * const refundTool = new SecureTool({\n * name: 'process_refund',\n * description: 'Processes a customer refund',\n * actor: 'invoice_bot',\n * requiredPermission: 'invoice:refund',\n * client: leluClient,\n * func: async (input) => {\n * // your tool implementation\n * return `Refund processed for ${input}`;\n * },\n * });\n * ```\n */\n\nimport { LeluClient } from \"../client.js\";\n\n// ─── Minimal LangChain-compatible interface ───────────────────────────────────\n\n/** Minimal interface a wrapped tool must expose. */\nexport interface ToolLike {\n name: string;\n description: string;\n /** LangChain StructuredTool / DynamicTool use `invoke`. */\n invoke?: (input: string, ...args: unknown[]) => Promise<string>;\n /** Older LangChain versions use `call`. */\n call?: (input: string, ...args: unknown[]) => Promise<string>;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\nexport interface SecureToolOptions {\n /** Unique tool name (used as the action in authz request). */\n name: string;\n /** Human-readable description forwarded to the LLM. */\n description: string;\n /** The Lelu agent scope / actor name. */\n actor: string;\n /** The permission string being checked (e.g. \"invoice:refund\"). */\n requiredPermission: string;\n /** Configured LeluClient. */\n client: LeluClient;\n /** The actual tool function to execute when authorized. */\n func: (input: string) => Promise<string>;\n /**\n * Optional: LLM confidence score for this invocation (0.0–1.0).\n * If omitted, defaults to 1.0 (full confidence assumed).\n */\n confidence?: number;\n /**\n * Optional: the human user the agent is acting on behalf of.\n */\n actingFor?: string;\n /**\n * Optional: if true, throw an error when denied instead of returning\n * a structured refusal string. Default: false (silent fail, structured msg).\n */\n throwOnDeny?: boolean;\n}\n\n// ─── Result ───────────────────────────────────────────────────────────────────\n\nexport interface ToolCallResult {\n allowed: boolean;\n output: string;\n requiresHumanReview: boolean;\n reviewId?: string;\n reason: string;\n}\n\n// ─── SecureTool ───────────────────────────────────────────────────────────────\n\n/**\n * SecureTool wraps a tool function with Lelu's Confidence-Aware Auth gate.\n *\n * Implements the LangChain Tool interface (name + description + invoke)\n * so it can be dropped into any LangChain agent tool array:\n * ```typescript\n * const agent = await createOpenAIFunctionsAgent({ tools: [refundTool] });\n * ```\n */\nexport class SecureTool {\n readonly name: string;\n readonly description: string;\n\n private readonly opts: SecureToolOptions;\n\n constructor(opts: SecureToolOptions) {\n this.opts = opts;\n this.name = opts.name;\n this.description = opts.description;\n }\n\n /**\n * invoke — LangChain StructuredTool / DynamicTool compatible entry point.\n *\n * 1. Calls Lelu `agentAuthorize` with the confidence score\n * 2a. Allowed → runs the wrapped tool function\n * 2b. Requires human review → returns structured \"pending\" message\n * 2c. Denied → returns structured refusal string (LLM can self-correct)\n */\n async invoke(input: string): Promise<string> {\n const result = await this._check(input);\n if (result.allowed) {\n return result.output;\n }\n if (this.opts.throwOnDeny) {\n throw new Error(`[Lelu] Tool \"${this.name}\" denied: ${result.reason}`);\n }\n return result.output; // structured refusal or pending message\n }\n\n /** call — alias for older LangChain versions. */\n async call(input: string): Promise<string> {\n return this.invoke(input);\n }\n\n /**\n * Check authorization and run the tool if permitted.\n * Returns a full ToolCallResult for programmatic consumers.\n */\n async checkAndCall(input: string): Promise<ToolCallResult> {\n return this._check(input);\n }\n\n private async _check(input: string): Promise<ToolCallResult> {\n const { actor, requiredPermission, client, func, throwOnDeny } = this.opts;\n const confidence = this.opts.confidence ?? 1.0;\n const actingFor = this.opts.actingFor ?? \"\";\n\n let decision;\n try {\n decision = await client.agentAuthorize({\n actor,\n action: requiredPermission,\n context: {\n confidence,\n actingFor,\n },\n });\n } catch (err) {\n const msg = `[Lelu] Authorization check failed for \"${this.name}\": ${String(err)}`;\n if (throwOnDeny) throw new Error(msg);\n return {\n allowed: false,\n output: msg,\n requiresHumanReview: false,\n reason: String(err),\n };\n }\n\n // Human review required — queue and return pending message.\n if (decision.requiresHumanReview) {\n const msg =\n `[Lelu] Action \"${this.name}\" is queued for human review. ` +\n `Reason: ${decision.reason}. Please wait for approval before proceeding.`;\n return {\n allowed: false,\n output: msg,\n requiresHumanReview: true,\n reason: decision.reason,\n };\n }\n\n // Hard deny — return structured refusal.\n if (!decision.allowed) {\n const msg =\n `[Lelu] Action \"${this.name}\" was denied. ` +\n `Reason: ${decision.reason}. ` +\n `Downgraded scope: ${decision.downgradedScope ?? \"none\"}.`;\n return {\n allowed: false,\n output: msg,\n requiresHumanReview: false,\n reason: decision.reason,\n };\n }\n\n // Authorized — run the tool.\n const output = await func(input);\n return {\n allowed: true,\n output,\n requiresHumanReview: false,\n reason: decision.reason,\n };\n }\n}\n","import { OpenAI } from 'openai';\n\nexport class SemanticPolicyGenerator {\n private openai: OpenAI;\n\n constructor(apiKey: string) {\n this.openai = new OpenAI({ apiKey });\n }\n\n /**\n * Converts a natural language description into a deterministic Rego policy.\n * \n * @param description Natural language description of the policy (e.g., \"Don't let the bot refund more than $50 unless approved by a finance manager.\")\n * @param packageName The Rego package name (default: \"lelu.authz\")\n * @returns The generated Rego policy string\n */\n async generateRegoPolicy(description: string, packageName: string = \"lelu.authz\"): Promise<string> {\n const prompt = `\nYou are an expert in Open Policy Agent (OPA) Rego policies.\nYour task is to convert the following natural language description into a valid, deterministic Rego policy for the Lelu Auth Permission Engine.\n\nThe policy must output an object with the following structure:\n{\n \"allowed\": bool,\n \"reason\": string,\n \"downgraded_scope\": string (optional),\n \"requires_human_review\": bool (optional)\n}\n\nThe input to the policy will be an object with the following structure:\n{\n \"kind\": \"agent\" | \"human\",\n \"actor\": string,\n \"action\": string,\n \"resource\": map[string]any,\n \"confidence\": number (0.0 to 1.0),\n \"acting_for\": string,\n \"scope\": string\n}\n\nNatural Language Description:\n\"${description}\"\n\nPackage Name:\npackage ${packageName}\n\nRequirements:\n1. Output ONLY valid Rego code.\n2. Do not include markdown formatting (like \\`\\`\\`rego).\n3. Ensure the default state is deny (default allowed = false).\n4. Include comments explaining the logic.\n`;\n\n const response = await this.openai.chat.completions.create({\n model: \"gpt-4-turbo-preview\",\n messages: [\n { role: \"system\", content: \"You are a Rego policy generation assistant. Output only raw Rego code.\" },\n { role: \"user\", content: prompt }\n ],\n temperature: 0.1,\n });\n\n const regoCode = response.choices[0]?.message.content?.trim() || \"\";\n \n // Strip markdown code blocks if the LLM accidentally included them\n return regoCode.replace(/^```rego\\n/, '').replace(/\\n```$/, '');\n }\n}\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface ApprovalRequest {
|
|
4
|
+
id: string;
|
|
5
|
+
agentId: string;
|
|
6
|
+
action: string;
|
|
7
|
+
resource: Record<string, any>;
|
|
8
|
+
confidence: number;
|
|
9
|
+
reason: string;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
}
|
|
12
|
+
interface LeluApprovalUIProps {
|
|
13
|
+
apiBaseUrl?: string;
|
|
14
|
+
onApprove?: (request: ApprovalRequest) => void;
|
|
15
|
+
onDeny?: (request: ApprovalRequest) => void;
|
|
16
|
+
pollIntervalMs?: number;
|
|
17
|
+
}
|
|
18
|
+
declare const LeluApprovalUI: React.FC<LeluApprovalUIProps>;
|
|
19
|
+
|
|
20
|
+
interface AgentReputationDashboardProps {
|
|
21
|
+
apiBaseUrl?: string;
|
|
22
|
+
refreshIntervalMs?: number;
|
|
23
|
+
}
|
|
24
|
+
declare const AgentReputationDashboard: React.FC<AgentReputationDashboardProps>;
|
|
25
|
+
|
|
26
|
+
interface UseAgentPermissionOptions {
|
|
27
|
+
/** Base URL of the Lelu engine. Defaults to http://localhost:8080 */
|
|
28
|
+
baseUrl?: string;
|
|
29
|
+
/** API key forwarded to the engine */
|
|
30
|
+
apiKey?: string;
|
|
31
|
+
/** Scope override passed in the request */
|
|
32
|
+
scope?: string;
|
|
33
|
+
}
|
|
34
|
+
interface AgentPermissionState {
|
|
35
|
+
canExecute: boolean;
|
|
36
|
+
loading: boolean;
|
|
37
|
+
reason: string;
|
|
38
|
+
decision: "allowed" | "denied" | "human_review" | "";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* React hook that checks whether an agent actor may perform an action.
|
|
42
|
+
*
|
|
43
|
+
* ```tsx
|
|
44
|
+
* const { canExecute, loading, reason } = useAgentPermission(
|
|
45
|
+
* "agent-007", "files.read", 0.95,
|
|
46
|
+
* { baseUrl: "http://localhost:8080", apiKey: process.env.NEXT_PUBLIC_LELU_KEY }
|
|
47
|
+
* );
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare function useAgentPermission(actor: string, action: string, confidence?: number, opts?: UseAgentPermissionOptions): AgentPermissionState;
|
|
51
|
+
|
|
52
|
+
export { type AgentPermissionState, AgentReputationDashboard, LeluApprovalUI, type UseAgentPermissionOptions, useAgentPermission };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface ApprovalRequest {
|
|
4
|
+
id: string;
|
|
5
|
+
agentId: string;
|
|
6
|
+
action: string;
|
|
7
|
+
resource: Record<string, any>;
|
|
8
|
+
confidence: number;
|
|
9
|
+
reason: string;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
}
|
|
12
|
+
interface LeluApprovalUIProps {
|
|
13
|
+
apiBaseUrl?: string;
|
|
14
|
+
onApprove?: (request: ApprovalRequest) => void;
|
|
15
|
+
onDeny?: (request: ApprovalRequest) => void;
|
|
16
|
+
pollIntervalMs?: number;
|
|
17
|
+
}
|
|
18
|
+
declare const LeluApprovalUI: React.FC<LeluApprovalUIProps>;
|
|
19
|
+
|
|
20
|
+
interface AgentReputationDashboardProps {
|
|
21
|
+
apiBaseUrl?: string;
|
|
22
|
+
refreshIntervalMs?: number;
|
|
23
|
+
}
|
|
24
|
+
declare const AgentReputationDashboard: React.FC<AgentReputationDashboardProps>;
|
|
25
|
+
|
|
26
|
+
interface UseAgentPermissionOptions {
|
|
27
|
+
/** Base URL of the Lelu engine. Defaults to http://localhost:8080 */
|
|
28
|
+
baseUrl?: string;
|
|
29
|
+
/** API key forwarded to the engine */
|
|
30
|
+
apiKey?: string;
|
|
31
|
+
/** Scope override passed in the request */
|
|
32
|
+
scope?: string;
|
|
33
|
+
}
|
|
34
|
+
interface AgentPermissionState {
|
|
35
|
+
canExecute: boolean;
|
|
36
|
+
loading: boolean;
|
|
37
|
+
reason: string;
|
|
38
|
+
decision: "allowed" | "denied" | "human_review" | "";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* React hook that checks whether an agent actor may perform an action.
|
|
42
|
+
*
|
|
43
|
+
* ```tsx
|
|
44
|
+
* const { canExecute, loading, reason } = useAgentPermission(
|
|
45
|
+
* "agent-007", "files.read", 0.95,
|
|
46
|
+
* { baseUrl: "http://localhost:8080", apiKey: process.env.NEXT_PUBLIC_LELU_KEY }
|
|
47
|
+
* );
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare function useAgentPermission(actor: string, action: string, confidence?: number, opts?: UseAgentPermissionOptions): AgentPermissionState;
|
|
51
|
+
|
|
52
|
+
export { type AgentPermissionState, AgentReputationDashboard, LeluApprovalUI, type UseAgentPermissionOptions, useAgentPermission };
|