agent-contracts-runtime
Version:
Runtime bridge for executing agent-contracts workflows on Agent SDKs
321 lines (319 loc) • 9.85 kB
JavaScript
// src/lib/plugin.ts
var PluginRegistry = class {
plugins = [];
register(plugin) {
if (this.plugins.some((p) => p.id === plugin.id)) {
throw new Error(`Plugin "${plugin.id}" is already registered`);
}
this.plugins.push(plugin);
}
getAll() {
return this.plugins;
}
async runBeforeTask(taskId, context) {
let ctx = context;
for (const plugin of this.plugins) {
if (!plugin.beforeTask || ctx === null) continue;
ctx = await plugin.beforeTask(taskId, ctx);
}
return ctx;
}
async runAfterTask(taskId, outcome) {
let result = outcome;
for (const plugin of this.plugins) {
if (!plugin.afterTask) continue;
result = await plugin.afterTask(taskId, result);
}
return result;
}
applyContextEnhancers(taskId, context) {
let ctx = context;
for (const plugin of this.plugins) {
if (!plugin.contextEnhancer) continue;
ctx = plugin.contextEnhancer(taskId, ctx);
}
return ctx;
}
/**
* Try plugin promptBuilder hooks. Returns custom prompt if any plugin provides one,
* or null to use the default buildTaskPrompt.
*/
applyPromptBuilder(args) {
for (const plugin of this.plugins) {
if (!plugin.promptBuilder) continue;
const result = plugin.promptBuilder(args);
if (result !== null) return result;
}
return null;
}
applyPromptEnhancers(taskId, prompt, context) {
let enhanced = prompt;
for (const plugin of this.plugins) {
if (!plugin.promptEnhancer) continue;
enhanced = plugin.promptEnhancer(taskId, enhanced, context);
}
return enhanced;
}
evaluateCommandGuardrails(command) {
const results = [];
for (const plugin of this.plugins) {
if (!plugin.customGuardrails?.evaluateCommand) continue;
results.push(...plugin.customGuardrails.evaluateCommand(command));
}
return results;
}
evaluateFilePathGuardrails(filePath) {
const results = [];
for (const plugin of this.plugins) {
if (!plugin.customGuardrails?.evaluateFilePath) continue;
results.push(...plugin.customGuardrails.evaluateFilePath(filePath));
}
return results;
}
evaluateFileContentGuardrails(filePath, content) {
const results = [];
for (const plugin of this.plugins) {
if (!plugin.customGuardrails?.evaluateFileContent) continue;
results.push(...plugin.customGuardrails.evaluateFileContent(filePath, content));
}
return results;
}
async runBeforeWorkflow(workflowId, userRequest) {
for (const plugin of this.plugins) {
if (!plugin.beforeWorkflow) continue;
await plugin.beforeWorkflow(workflowId, userRequest);
}
}
async runAfterWorkflow(workflowId, result) {
for (const plugin of this.plugins) {
if (!plugin.afterWorkflow) continue;
await plugin.afterWorkflow(workflowId, result);
}
}
};
var pluginRegistry = new PluginRegistry();
// src/lib/guardrail-hooks.ts
function globFragmentToRegex(fragment) {
let out = "";
for (const ch of fragment) {
if (ch === "*") out += "[^/]*";
else if (ch === "?") out += "[^/]";
else if (".+^$|()[]\\".includes(ch)) out += "\\" + ch;
else out += ch;
}
return out;
}
function matchGlob(pattern, path) {
let regex = "";
let i = 0;
while (i < pattern.length) {
if (pattern[i] === "*" && pattern[i + 1] === "*" && pattern[i + 2] === "/") {
regex += "(?:.+/)?";
i += 3;
} else if (pattern[i] === "*" && pattern[i + 1] === "*") {
regex += ".*";
i += 2;
} else if (pattern[i] === "*") {
regex += "[^/]*";
i += 1;
} else if (pattern[i] === "?") {
regex += "[^/]";
i += 1;
} else if (pattern[i] === "{") {
const end = pattern.indexOf("}", i);
if (end !== -1) {
const alts = pattern.slice(i + 1, end).split(",").map(globFragmentToRegex);
regex += "(?:" + alts.join("|") + ")";
i = end + 1;
} else {
regex += "\\{";
i += 1;
}
} else if (".+^$|()[]\\".includes(pattern[i])) {
regex += "\\" + pattern[i];
i += 1;
} else {
regex += pattern[i];
i += 1;
}
}
return new RegExp("^" + regex + "$").test(path);
}
function getBlockingViolations(results) {
return results.filter((r) => !r.passed && r.action === "block");
}
function getWarnings(results) {
return results.filter((r) => !r.passed && r.action === "warn");
}
function hasBlockingViolation(results) {
return results.some((r) => !r.passed && r.action === "block");
}
function pluginResultToCheckResult(r) {
return {
guardrail_id: r.guardrail_id,
passed: !r.matched,
action: r.action,
message: r.matched ? r.message : ""
};
}
function createGuardrailHooks(checks) {
function runChecks(ctx) {
const results = [];
if (ctx.command) {
results.push(...checks.checkCommand(ctx.command));
results.push(
...pluginRegistry.evaluateCommandGuardrails(ctx.command).map(pluginResultToCheckResult)
);
}
if (ctx.filePath) {
results.push(...checks.checkFilePath(ctx.filePath));
results.push(
...pluginRegistry.evaluateFilePathGuardrails(ctx.filePath).map(pluginResultToCheckResult)
);
}
if (ctx.filePath && ctx.content) {
results.push(...checks.checkContent(ctx.filePath, ctx.content));
results.push(
...pluginRegistry.evaluateFileContentGuardrails(ctx.filePath, ctx.content).map(pluginResultToCheckResult)
);
}
return results;
}
function beforeShellExecution(input) {
const command = input.command ?? "";
if (!command) return {};
const results = runChecks({ command });
const blocks = getBlockingViolations(results);
const warns = getWarnings(results);
if (blocks.length > 0) {
const msg = blocks.map((r) => `[${r.guardrail_id}] ${r.message}`).join("\n");
return { permission: "deny", user_message: msg, agent_message: msg };
}
if (warns.length > 0) {
return {
additionalContext: warns.map((r) => `[${r.guardrail_id}] ${r.message}`).join("\n")
};
}
return {};
}
function preToolUse(input) {
const filePath = input.tool_input?.file_path ?? input.tool_input?.path ?? "";
if (!filePath) return {};
const results = runChecks({ filePath });
const blocks = getBlockingViolations(results);
if (blocks.length > 0) {
const msg = blocks.map((r) => `[${r.guardrail_id}] ${r.message}`).join("\n");
return { permission: "deny", user_message: msg, agent_message: msg };
}
return {};
}
function afterFileEdit(input) {
const filePath = input.file_path ?? "";
const content = input.content ?? "";
if (!filePath) return {};
const results = runChecks({ filePath, content });
const blocks = getBlockingViolations(results);
if (blocks.length > 0) {
const msg = blocks.map((r) => `[${r.guardrail_id}] ${r.message}`).join("\n");
return { permission: "deny", user_message: msg, agent_message: msg };
}
return {};
}
return { beforeShellExecution, preToolUse, afterFileEdit, runChecks };
}
function createGuardrailHooksForAgent(agentId, perAgent) {
const rules = perAgent.get(agentId);
if (!rules) {
return createGuardrailHooks({
checkCommand: () => [],
checkFilePath: () => [],
checkContent: () => []
});
}
const hooks = createGuardrailHooksFromRules(rules);
const baseRunChecks = hooks.runChecks;
return {
...hooks,
runChecks(ctx) {
return baseRunChecks({ ...ctx, agentId });
}
};
}
function createGuardrailHooksFromRules(rules) {
const commandChecks = rules.commandRules.map((rule) => ({
guardrail_id: rule.guardrail_id,
pattern: new RegExp(rule.pattern),
action: rule.action,
message: rule.message
}));
const fileChecks = rules.fileRules;
const contentChecks = rules.contentRules.map((rule) => ({
guardrail_id: rule.guardrail_id,
pattern: new RegExp(rule.pattern),
action: rule.action,
message: rule.message,
file_glob: rule.file_glob,
exclude_glob: rule.exclude_glob
}));
return createGuardrailHooks({
checkCommand(command) {
const results = [];
for (const check of commandChecks) {
const passed = !check.pattern.test(command);
results.push({
guardrail_id: check.guardrail_id,
passed,
action: check.action,
message: passed ? "" : check.message
});
}
return results;
},
checkFilePath(filePath) {
const results = [];
for (const check of fileChecks) {
if (check.exclude_glob && matchGlob(check.exclude_glob, filePath)) {
continue;
}
const matched = matchGlob(check.pattern, filePath);
results.push({
guardrail_id: check.guardrail_id,
passed: !matched,
action: check.action,
message: matched ? check.message : ""
});
}
return results;
},
checkContent(filePath, content) {
const results = [];
for (const check of contentChecks) {
if (check.file_glob && !matchGlob(check.file_glob, filePath)) {
continue;
}
if (check.exclude_glob && matchGlob(check.exclude_glob, filePath)) {
continue;
}
const matched = check.pattern.test(content);
results.push({
guardrail_id: check.guardrail_id,
passed: !matched,
action: check.action,
message: matched ? check.message : ""
});
}
return results;
}
});
}
export {
PluginRegistry,
pluginRegistry,
getBlockingViolations,
getWarnings,
hasBlockingViolation,
createGuardrailHooks,
createGuardrailHooksForAgent,
createGuardrailHooksFromRules
};
//# sourceMappingURL=chunk-MGRZBGQL.js.map