@alfe.ai/openclaw-identity 0.0.15 → 0.0.17
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.d.cts +32 -8
- package/dist/index.d.ts +32 -8
- package/dist/plugin.d.cts +1 -0
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin2.cjs +81 -13
- package/dist/plugin2.js +81 -13
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
import plugin from "./plugin.cjs";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/runtime-contract.d.ts
|
|
4
4
|
|
|
5
|
-
type IdentityFailureMode = "open" | "closed" | "permissive";
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* OpenClaw runtime contract for the identity plugin's `before_tool_call` hook.
|
|
7
|
+
*
|
|
8
|
+
* Phase 1 Stage A.0 of the roles/permissions/sessions plan. The plugin's hook
|
|
9
|
+
* signature documents what the OpenClaw daemon must populate when a tool call
|
|
10
|
+
* is dispatched. The actual gate logic (Stage E) reads these fields to make a
|
|
11
|
+
* permit/deny decision via `@auriclabs/roles`.
|
|
12
|
+
*
|
|
13
|
+
* Contract:
|
|
14
|
+
* - `ToolCallEvent` describes the tool being called (name + args).
|
|
15
|
+
* - `ToolCallContext` describes the calling context (tenant, agent, actor).
|
|
16
|
+
*
|
|
17
|
+
* The hook receives `(event, ctx)`. Both arguments are required. The daemon
|
|
18
|
+
* is responsible for populating every required field; the plugin's gate
|
|
19
|
+
* applies the per-agent `IdentityFailureMode` when an optional field is
|
|
20
|
+
* absent and a permission decision can't be made cleanly.
|
|
21
|
+
*
|
|
22
|
+
* Round 6 note (gate context shape): the gate context passed to `sift` is
|
|
23
|
+
* `{ tool: event.toolName, args: event.toolArgs }` with strict namespacing
|
|
24
|
+
* (no spread). This prevents an attacker-controlled `tool` field inside
|
|
25
|
+
* `toolArgs` from overriding the gate's value.
|
|
9
26
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Per-agent fallback policy when an optional `ctx.actingIdentityId` is
|
|
29
|
+
* absent (e.g. autonomous tool_chain runs that don't carry a chat-side
|
|
30
|
+
* sender). Round-5 unified plan literals.
|
|
31
|
+
*
|
|
32
|
+
* - "open" — admit the call (legacy default).
|
|
33
|
+
* - "closed" — block the call (production default once Stage E ships).
|
|
34
|
+
* - "permissive" — admit with a warn-level audit log; intermediate during
|
|
35
|
+
* the Stage H dwell window.
|
|
36
|
+
*/
|
|
37
|
+
type IdentityFailureMode = "open" | "closed" | "permissive";
|
|
14
38
|
/**
|
|
15
39
|
* The tool the agent is about to call.
|
|
16
40
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
import plugin from "./plugin.js";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/runtime-contract.d.ts
|
|
4
4
|
|
|
5
|
-
type IdentityFailureMode = "open" | "closed" | "permissive";
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* OpenClaw runtime contract for the identity plugin's `before_tool_call` hook.
|
|
7
|
+
*
|
|
8
|
+
* Phase 1 Stage A.0 of the roles/permissions/sessions plan. The plugin's hook
|
|
9
|
+
* signature documents what the OpenClaw daemon must populate when a tool call
|
|
10
|
+
* is dispatched. The actual gate logic (Stage E) reads these fields to make a
|
|
11
|
+
* permit/deny decision via `@auriclabs/roles`.
|
|
12
|
+
*
|
|
13
|
+
* Contract:
|
|
14
|
+
* - `ToolCallEvent` describes the tool being called (name + args).
|
|
15
|
+
* - `ToolCallContext` describes the calling context (tenant, agent, actor).
|
|
16
|
+
*
|
|
17
|
+
* The hook receives `(event, ctx)`. Both arguments are required. The daemon
|
|
18
|
+
* is responsible for populating every required field; the plugin's gate
|
|
19
|
+
* applies the per-agent `IdentityFailureMode` when an optional field is
|
|
20
|
+
* absent and a permission decision can't be made cleanly.
|
|
21
|
+
*
|
|
22
|
+
* Round 6 note (gate context shape): the gate context passed to `sift` is
|
|
23
|
+
* `{ tool: event.toolName, args: event.toolArgs }` with strict namespacing
|
|
24
|
+
* (no spread). This prevents an attacker-controlled `tool` field inside
|
|
25
|
+
* `toolArgs` from overriding the gate's value.
|
|
9
26
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Per-agent fallback policy when an optional `ctx.actingIdentityId` is
|
|
29
|
+
* absent (e.g. autonomous tool_chain runs that don't carry a chat-side
|
|
30
|
+
* sender). Round-5 unified plan literals.
|
|
31
|
+
*
|
|
32
|
+
* - "open" — admit the call (legacy default).
|
|
33
|
+
* - "closed" — block the call (production default once Stage E ships).
|
|
34
|
+
* - "permissive" — admit with a warn-level audit log; intermediate during
|
|
35
|
+
* the Stage H dwell window.
|
|
36
|
+
*/
|
|
37
|
+
type IdentityFailureMode = "open" | "closed" | "permissive";
|
|
14
38
|
/**
|
|
15
39
|
* The tool the agent is about to call.
|
|
16
40
|
*
|
package/dist/plugin.d.cts
CHANGED
package/dist/plugin.d.ts
CHANGED
package/dist/plugin2.cjs
CHANGED
|
@@ -82,21 +82,49 @@ function resolveToolGatingMode() {
|
|
|
82
82
|
if (raw === "fail-open" || raw === "fail-closed") return raw;
|
|
83
83
|
return "fail-open";
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Evaluate `agent:chat` — the inbound conversational gate (Layer 2 of the
|
|
87
|
+
* default-deny chat path). Mirrors `evaluateAgentExec` but with no
|
|
88
|
+
* tool/args context — chat admission only depends on whether the
|
|
89
|
+
* resolved identity holds `agent:chat` at the agent's scope.
|
|
90
|
+
*
|
|
91
|
+
* Layer 1 (`services/chat`) already gates this for chat WS + channel
|
|
92
|
+
* adapter inject. The OpenClaw plugin's `message_received` hook is
|
|
93
|
+
* defense-in-depth — it catches any path that reaches the agent
|
|
94
|
+
* runtime without going through Layer 1.
|
|
95
|
+
*/
|
|
96
|
+
function evaluateAgentChat(permissions, args) {
|
|
97
|
+
if (!args.agentId) return { allowed: false };
|
|
98
|
+
const ability = (0, _auriclabs_roles.createAbility)(permissions);
|
|
99
|
+
const gatePermission = {
|
|
100
|
+
subject: "agent",
|
|
101
|
+
action: "chat",
|
|
102
|
+
scope: `agent:${args.agentId}`
|
|
103
|
+
};
|
|
104
|
+
return { allowed: ability.has(gatePermission, {}) };
|
|
105
|
+
}
|
|
85
106
|
//#endregion
|
|
86
107
|
//#region src/plugin.ts
|
|
87
108
|
/**
|
|
88
109
|
* @alfe.ai/openclaw-identity — OpenClaw native plugin
|
|
89
110
|
*
|
|
90
|
-
* HTTP-based identity resolution +
|
|
111
|
+
* HTTP-based identity resolution + role-based admission gate. Installed as
|
|
91
112
|
* part of the core alfe integration on every agent.
|
|
92
113
|
*
|
|
93
114
|
* Hooks:
|
|
94
|
-
* - message_received →
|
|
95
|
-
*
|
|
115
|
+
* - message_received → resolves the sender via the agent API and gates
|
|
116
|
+
* on `agent:chat` at the agent's scope using the
|
|
117
|
+
* resolved identity's permission set
|
|
118
|
+
* (`evaluateAgentChat`). Blocks unknown senders
|
|
119
|
+
* and any identity without the permission. Layer 2
|
|
120
|
+
* of the default-deny chat path.
|
|
96
121
|
* - before_tool_call → typed `(event: ToolCallEvent, ctx: ToolCallContext)`
|
|
97
|
-
* hook (Phase 1 Stage A.0).
|
|
98
|
-
*
|
|
99
|
-
*
|
|
122
|
+
* hook (Phase 1 Stage A.0). Gates `agent:exec`
|
|
123
|
+
* via `evaluateAgentExec` against the token's
|
|
124
|
+
* own permissions (token path) or the cached
|
|
125
|
+
* resolved permissions (JWT path). Sift
|
|
126
|
+
* conditions on the permission narrow which
|
|
127
|
+
* tools the call may invoke.
|
|
100
128
|
*
|
|
101
129
|
* `after_tool_call` is intentionally absent — tool-call audit lives in a
|
|
102
130
|
* future dedicated audit service, not in identity.
|
|
@@ -152,6 +180,8 @@ function defineTool(def) {
|
|
|
152
180
|
}
|
|
153
181
|
};
|
|
154
182
|
}
|
|
183
|
+
const ACTIVATED_KEY = "__alfeIdentityPluginActivated";
|
|
184
|
+
const g = globalThis;
|
|
155
185
|
const plugin = {
|
|
156
186
|
id: "@alfe.ai/openclaw-identity",
|
|
157
187
|
name: "Alfe Identity",
|
|
@@ -159,6 +189,11 @@ const plugin = {
|
|
|
159
189
|
version: pkg.version,
|
|
160
190
|
activate(api) {
|
|
161
191
|
const log = api.logger;
|
|
192
|
+
if (g[ACTIVATED_KEY] === true) {
|
|
193
|
+
log.debug("Alfe Identity plugin already activated — skipping duplicate");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
g[ACTIVATED_KEY] = true;
|
|
162
197
|
log.info("Alfe Identity plugin activating...");
|
|
163
198
|
let client;
|
|
164
199
|
try {
|
|
@@ -168,6 +203,7 @@ const plugin = {
|
|
|
168
203
|
apiUrl: config.apiUrl
|
|
169
204
|
});
|
|
170
205
|
} catch (err) {
|
|
206
|
+
g[ACTIVATED_KEY] = false;
|
|
171
207
|
log.error(`Identity plugin: failed to resolve config — ${err instanceof Error ? err.message : String(err)}`);
|
|
172
208
|
return;
|
|
173
209
|
}
|
|
@@ -353,6 +389,15 @@ const plugin = {
|
|
|
353
389
|
const provider = ctx.channelId ?? "unknown";
|
|
354
390
|
const senderId = event.metadata?.UserId ?? event.from;
|
|
355
391
|
if (!senderId) return;
|
|
392
|
+
const agentId = ctx.agentId;
|
|
393
|
+
if (!agentId) {
|
|
394
|
+
log.warn(`message_received without ctx.agentId — fail-closed`);
|
|
395
|
+
return {
|
|
396
|
+
block: true,
|
|
397
|
+
blockReason: "Identity: agentId missing on message context"
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const forwarded = Array.isArray(event.metadata?.SenderPermissions) ? event.metadata.SenderPermissions.filter((p) => typeof p === "string") : [];
|
|
356
401
|
const cacheKey = ctx.conversationId ?? `${provider}:${senderId}`;
|
|
357
402
|
const cached = getCachedResolve(cacheKey);
|
|
358
403
|
if (cached) {
|
|
@@ -360,9 +405,9 @@ const plugin = {
|
|
|
360
405
|
block: true,
|
|
361
406
|
blockReason: "Identity: identity not provisioned for this channel"
|
|
362
407
|
};
|
|
363
|
-
if (!cached.
|
|
408
|
+
if (!evaluateAgentChat([...cached.permissions, ...forwarded], { agentId }).allowed) return {
|
|
364
409
|
block: true,
|
|
365
|
-
blockReason: "Identity:
|
|
410
|
+
blockReason: "Identity: chat not permitted for this sender"
|
|
366
411
|
};
|
|
367
412
|
return;
|
|
368
413
|
}
|
|
@@ -373,8 +418,8 @@ const plugin = {
|
|
|
373
418
|
});
|
|
374
419
|
setCachedResolve(cacheKey, {
|
|
375
420
|
identityId: r.identityId,
|
|
376
|
-
|
|
377
|
-
|
|
421
|
+
status: r.status,
|
|
422
|
+
permissions: [...r.permissions, ...forwarded]
|
|
378
423
|
});
|
|
379
424
|
if (r.identityId == null) {
|
|
380
425
|
log.warn(`Identity not provisioned for ${provider}:${senderId} — blocking inbound message`);
|
|
@@ -383,9 +428,10 @@ const plugin = {
|
|
|
383
428
|
blockReason: "Identity: identity not provisioned for this channel"
|
|
384
429
|
};
|
|
385
430
|
}
|
|
386
|
-
|
|
431
|
+
const cachedAfterFill = getCachedResolve(cacheKey);
|
|
432
|
+
if (!evaluateAgentChat(cachedAfterFill ? [...cachedAfterFill.permissions] : [...r.permissions, ...forwarded], { agentId }).allowed) return {
|
|
387
433
|
block: true,
|
|
388
|
-
blockReason: "Identity:
|
|
434
|
+
blockReason: "Identity: chat not permitted for this sender"
|
|
389
435
|
};
|
|
390
436
|
log.info(`Identity resolved: ${senderId} → ${r.identityId} (${r.status})`);
|
|
391
437
|
} catch (e) {
|
|
@@ -405,10 +451,32 @@ const plugin = {
|
|
|
405
451
|
}), resolveToolGatingMode(), event.toolName);
|
|
406
452
|
return Promise.resolve(blocked);
|
|
407
453
|
}
|
|
408
|
-
|
|
454
|
+
const cacheKey = ctx.conversationId ?? (ctx.channelId && ctx.actingIdentityId ? `${ctx.channelId}:${ctx.actingIdentityId}` : null);
|
|
455
|
+
if (cacheKey) {
|
|
456
|
+
const cached = getCachedResolve(cacheKey);
|
|
457
|
+
if (cached && cached.permissions.length > 0) {
|
|
458
|
+
const blocked = applyGateMode(evaluateAgentExec([...cached.permissions], {
|
|
459
|
+
agentId: ctx.agentId,
|
|
460
|
+
toolName: event.toolName,
|
|
461
|
+
toolArgs: event.toolArgs
|
|
462
|
+
}), resolveToolGatingMode(), event.toolName);
|
|
463
|
+
return Promise.resolve(blocked);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const fallback = evaluateAgentExec([], {
|
|
467
|
+
agentId: ctx.agentId,
|
|
468
|
+
toolName: event.toolName,
|
|
469
|
+
toolArgs: event.toolArgs
|
|
470
|
+
});
|
|
471
|
+
const mode = resolveToolGatingMode();
|
|
472
|
+
return Promise.resolve(applyGateMode(fallback, mode, event.toolName));
|
|
409
473
|
};
|
|
410
474
|
api.on("before_tool_call", (...args) => beforeToolCallStub(args[0], args[1]), { priority: 100 });
|
|
411
475
|
log.info("Alfe Identity plugin activated");
|
|
476
|
+
},
|
|
477
|
+
deactivate(api) {
|
|
478
|
+
g[ACTIVATED_KEY] = false;
|
|
479
|
+
api.logger.info("Alfe Identity plugin deactivated");
|
|
412
480
|
}
|
|
413
481
|
};
|
|
414
482
|
//#endregion
|
package/dist/plugin2.js
CHANGED
|
@@ -82,21 +82,49 @@ function resolveToolGatingMode() {
|
|
|
82
82
|
if (raw === "fail-open" || raw === "fail-closed") return raw;
|
|
83
83
|
return "fail-open";
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Evaluate `agent:chat` — the inbound conversational gate (Layer 2 of the
|
|
87
|
+
* default-deny chat path). Mirrors `evaluateAgentExec` but with no
|
|
88
|
+
* tool/args context — chat admission only depends on whether the
|
|
89
|
+
* resolved identity holds `agent:chat` at the agent's scope.
|
|
90
|
+
*
|
|
91
|
+
* Layer 1 (`services/chat`) already gates this for chat WS + channel
|
|
92
|
+
* adapter inject. The OpenClaw plugin's `message_received` hook is
|
|
93
|
+
* defense-in-depth — it catches any path that reaches the agent
|
|
94
|
+
* runtime without going through Layer 1.
|
|
95
|
+
*/
|
|
96
|
+
function evaluateAgentChat(permissions, args) {
|
|
97
|
+
if (!args.agentId) return { allowed: false };
|
|
98
|
+
const ability = createAbility(permissions);
|
|
99
|
+
const gatePermission = {
|
|
100
|
+
subject: "agent",
|
|
101
|
+
action: "chat",
|
|
102
|
+
scope: `agent:${args.agentId}`
|
|
103
|
+
};
|
|
104
|
+
return { allowed: ability.has(gatePermission, {}) };
|
|
105
|
+
}
|
|
85
106
|
//#endregion
|
|
86
107
|
//#region src/plugin.ts
|
|
87
108
|
/**
|
|
88
109
|
* @alfe.ai/openclaw-identity — OpenClaw native plugin
|
|
89
110
|
*
|
|
90
|
-
* HTTP-based identity resolution +
|
|
111
|
+
* HTTP-based identity resolution + role-based admission gate. Installed as
|
|
91
112
|
* part of the core alfe integration on every agent.
|
|
92
113
|
*
|
|
93
114
|
* Hooks:
|
|
94
|
-
* - message_received →
|
|
95
|
-
*
|
|
115
|
+
* - message_received → resolves the sender via the agent API and gates
|
|
116
|
+
* on `agent:chat` at the agent's scope using the
|
|
117
|
+
* resolved identity's permission set
|
|
118
|
+
* (`evaluateAgentChat`). Blocks unknown senders
|
|
119
|
+
* and any identity without the permission. Layer 2
|
|
120
|
+
* of the default-deny chat path.
|
|
96
121
|
* - before_tool_call → typed `(event: ToolCallEvent, ctx: ToolCallContext)`
|
|
97
|
-
* hook (Phase 1 Stage A.0).
|
|
98
|
-
*
|
|
99
|
-
*
|
|
122
|
+
* hook (Phase 1 Stage A.0). Gates `agent:exec`
|
|
123
|
+
* via `evaluateAgentExec` against the token's
|
|
124
|
+
* own permissions (token path) or the cached
|
|
125
|
+
* resolved permissions (JWT path). Sift
|
|
126
|
+
* conditions on the permission narrow which
|
|
127
|
+
* tools the call may invoke.
|
|
100
128
|
*
|
|
101
129
|
* `after_tool_call` is intentionally absent — tool-call audit lives in a
|
|
102
130
|
* future dedicated audit service, not in identity.
|
|
@@ -152,6 +180,8 @@ function defineTool(def) {
|
|
|
152
180
|
}
|
|
153
181
|
};
|
|
154
182
|
}
|
|
183
|
+
const ACTIVATED_KEY = "__alfeIdentityPluginActivated";
|
|
184
|
+
const g = globalThis;
|
|
155
185
|
const plugin = {
|
|
156
186
|
id: "@alfe.ai/openclaw-identity",
|
|
157
187
|
name: "Alfe Identity",
|
|
@@ -159,6 +189,11 @@ const plugin = {
|
|
|
159
189
|
version: pkg.version,
|
|
160
190
|
activate(api) {
|
|
161
191
|
const log = api.logger;
|
|
192
|
+
if (g[ACTIVATED_KEY] === true) {
|
|
193
|
+
log.debug("Alfe Identity plugin already activated — skipping duplicate");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
g[ACTIVATED_KEY] = true;
|
|
162
197
|
log.info("Alfe Identity plugin activating...");
|
|
163
198
|
let client;
|
|
164
199
|
try {
|
|
@@ -168,6 +203,7 @@ const plugin = {
|
|
|
168
203
|
apiUrl: config.apiUrl
|
|
169
204
|
});
|
|
170
205
|
} catch (err) {
|
|
206
|
+
g[ACTIVATED_KEY] = false;
|
|
171
207
|
log.error(`Identity plugin: failed to resolve config — ${err instanceof Error ? err.message : String(err)}`);
|
|
172
208
|
return;
|
|
173
209
|
}
|
|
@@ -353,6 +389,15 @@ const plugin = {
|
|
|
353
389
|
const provider = ctx.channelId ?? "unknown";
|
|
354
390
|
const senderId = event.metadata?.UserId ?? event.from;
|
|
355
391
|
if (!senderId) return;
|
|
392
|
+
const agentId = ctx.agentId;
|
|
393
|
+
if (!agentId) {
|
|
394
|
+
log.warn(`message_received without ctx.agentId — fail-closed`);
|
|
395
|
+
return {
|
|
396
|
+
block: true,
|
|
397
|
+
blockReason: "Identity: agentId missing on message context"
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const forwarded = Array.isArray(event.metadata?.SenderPermissions) ? event.metadata.SenderPermissions.filter((p) => typeof p === "string") : [];
|
|
356
401
|
const cacheKey = ctx.conversationId ?? `${provider}:${senderId}`;
|
|
357
402
|
const cached = getCachedResolve(cacheKey);
|
|
358
403
|
if (cached) {
|
|
@@ -360,9 +405,9 @@ const plugin = {
|
|
|
360
405
|
block: true,
|
|
361
406
|
blockReason: "Identity: identity not provisioned for this channel"
|
|
362
407
|
};
|
|
363
|
-
if (!cached.
|
|
408
|
+
if (!evaluateAgentChat([...cached.permissions, ...forwarded], { agentId }).allowed) return {
|
|
364
409
|
block: true,
|
|
365
|
-
blockReason: "Identity:
|
|
410
|
+
blockReason: "Identity: chat not permitted for this sender"
|
|
366
411
|
};
|
|
367
412
|
return;
|
|
368
413
|
}
|
|
@@ -373,8 +418,8 @@ const plugin = {
|
|
|
373
418
|
});
|
|
374
419
|
setCachedResolve(cacheKey, {
|
|
375
420
|
identityId: r.identityId,
|
|
376
|
-
|
|
377
|
-
|
|
421
|
+
status: r.status,
|
|
422
|
+
permissions: [...r.permissions, ...forwarded]
|
|
378
423
|
});
|
|
379
424
|
if (r.identityId == null) {
|
|
380
425
|
log.warn(`Identity not provisioned for ${provider}:${senderId} — blocking inbound message`);
|
|
@@ -383,9 +428,10 @@ const plugin = {
|
|
|
383
428
|
blockReason: "Identity: identity not provisioned for this channel"
|
|
384
429
|
};
|
|
385
430
|
}
|
|
386
|
-
|
|
431
|
+
const cachedAfterFill = getCachedResolve(cacheKey);
|
|
432
|
+
if (!evaluateAgentChat(cachedAfterFill ? [...cachedAfterFill.permissions] : [...r.permissions, ...forwarded], { agentId }).allowed) return {
|
|
387
433
|
block: true,
|
|
388
|
-
blockReason: "Identity:
|
|
434
|
+
blockReason: "Identity: chat not permitted for this sender"
|
|
389
435
|
};
|
|
390
436
|
log.info(`Identity resolved: ${senderId} → ${r.identityId} (${r.status})`);
|
|
391
437
|
} catch (e) {
|
|
@@ -405,10 +451,32 @@ const plugin = {
|
|
|
405
451
|
}), resolveToolGatingMode(), event.toolName);
|
|
406
452
|
return Promise.resolve(blocked);
|
|
407
453
|
}
|
|
408
|
-
|
|
454
|
+
const cacheKey = ctx.conversationId ?? (ctx.channelId && ctx.actingIdentityId ? `${ctx.channelId}:${ctx.actingIdentityId}` : null);
|
|
455
|
+
if (cacheKey) {
|
|
456
|
+
const cached = getCachedResolve(cacheKey);
|
|
457
|
+
if (cached && cached.permissions.length > 0) {
|
|
458
|
+
const blocked = applyGateMode(evaluateAgentExec([...cached.permissions], {
|
|
459
|
+
agentId: ctx.agentId,
|
|
460
|
+
toolName: event.toolName,
|
|
461
|
+
toolArgs: event.toolArgs
|
|
462
|
+
}), resolveToolGatingMode(), event.toolName);
|
|
463
|
+
return Promise.resolve(blocked);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const fallback = evaluateAgentExec([], {
|
|
467
|
+
agentId: ctx.agentId,
|
|
468
|
+
toolName: event.toolName,
|
|
469
|
+
toolArgs: event.toolArgs
|
|
470
|
+
});
|
|
471
|
+
const mode = resolveToolGatingMode();
|
|
472
|
+
return Promise.resolve(applyGateMode(fallback, mode, event.toolName));
|
|
409
473
|
};
|
|
410
474
|
api.on("before_tool_call", (...args) => beforeToolCallStub(args[0], args[1]), { priority: 100 });
|
|
411
475
|
log.info("Alfe Identity plugin activated");
|
|
476
|
+
},
|
|
477
|
+
deactivate(api) {
|
|
478
|
+
g[ACTIVATED_KEY] = false;
|
|
479
|
+
api.logger.info("Alfe Identity plugin deactivated");
|
|
412
480
|
}
|
|
413
481
|
};
|
|
414
482
|
//#endregion
|
package/package.json
CHANGED