92 lines
3.3 KiB
JavaScript
92 lines
3.3 KiB
JavaScript
const DEFAULT_READ_ONLY_TOOLS = Object.freeze([
|
|
"read", "grep", "glob", "find", "ls", "web_search", "web_fetch",
|
|
"memory_search", "sessions_list", "sessions_history", "session_status",
|
|
]);
|
|
|
|
function strings(value) {
|
|
return Array.isArray(value)
|
|
? value.filter((item) => typeof item === "string" && item.trim())
|
|
.map((item) => item.trim())
|
|
: [];
|
|
}
|
|
|
|
export function resolveSenderId(ctx = {}) {
|
|
const direct = typeof ctx.senderId === "string" ? ctx.senderId.trim() : "";
|
|
const nested = typeof ctx.channelContext?.sender?.id === "string"
|
|
? ctx.channelContext.sender.id.trim()
|
|
: "";
|
|
return direct || nested || undefined;
|
|
}
|
|
|
|
export function classifyRun(ctx = {}, config = {}) {
|
|
const senderId = resolveSenderId(ctx);
|
|
const trusted = new Set(strings(config.trustedSenderIds));
|
|
const blocked = new Set(strings(config.blockedSenderIds));
|
|
|
|
if (typeof ctx.jobId === "string" && ctx.jobId) {
|
|
return { trusted: false, origin: "automation", reason: "job_id_present" };
|
|
}
|
|
if (!senderId) {
|
|
return { trusted: false, origin: "system_or_unknown", reason: "sender_id_missing" };
|
|
}
|
|
if (blocked.has(senderId)) {
|
|
return { trusted: false, origin: "blocked_sender", reason: "sender_blocked", senderId };
|
|
}
|
|
if (config.requireExplicitTrustedSenders !== false && !trusted.has(senderId)) {
|
|
return { trusted: false, origin: "untrusted_sender", reason: "sender_not_allowlisted", senderId };
|
|
}
|
|
return {
|
|
trusted: true,
|
|
origin: "authenticated_channel_user",
|
|
reason: "sender_verified",
|
|
senderId,
|
|
};
|
|
}
|
|
|
|
export function decideToolCall(toolName, runTrust, config = {}) {
|
|
const name = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
const configured = strings(config.readOnlyTools).map((item) => item.toLowerCase());
|
|
const readOnly = new Set(configured.length ? configured : DEFAULT_READ_ONLY_TOOLS);
|
|
if (!name) {
|
|
return { action: "block", code: "tool_name_missing" };
|
|
}
|
|
if (readOnly.has(name)) {
|
|
return { action: "allow", code: "read_only" };
|
|
}
|
|
if (!runTrust || runTrust.trusted !== true) {
|
|
return {
|
|
action: "block",
|
|
code: runTrust?.reason || "run_provenance_missing",
|
|
};
|
|
}
|
|
if (config.requireApprovalForHumanSideEffects === false) {
|
|
return { action: "allow", code: "trusted_side_effect" };
|
|
}
|
|
return { action: "approval", code: "human_side_effect" };
|
|
}
|
|
|
|
export function runKey(event = {}, ctx = {}) {
|
|
const normalize = (value) => typeof value === "string" && value.trim()
|
|
? value.trim()
|
|
: undefined;
|
|
const sameField = (eventValue, contextValue) => {
|
|
const fromEvent = normalize(eventValue);
|
|
const fromContext = normalize(contextValue);
|
|
return {
|
|
valid: !(fromEvent && fromContext && fromEvent !== fromContext),
|
|
value: fromEvent || fromContext,
|
|
};
|
|
};
|
|
const runId = sameField(event.runId, ctx.runId);
|
|
const agentId = sameField(event.agentId, ctx.agentId);
|
|
const sessionId = sameField(event.sessionId, ctx.sessionId);
|
|
const sessionKey = sameField(event.sessionKey, ctx.sessionKey);
|
|
if (!runId.valid || !agentId.valid || !sessionId.valid || !sessionKey.valid) {
|
|
return undefined;
|
|
}
|
|
const selectedSession = sessionId.value || sessionKey.value;
|
|
return runId.value && agentId.value && selectedSession
|
|
? JSON.stringify([runId.value, agentId.value, selectedSession])
|
|
: undefined;
|
|
}
|