feat(policy): enforce truthfulness and autonomy guardrails

Add runtime truthfulness modes and autonomy-level tool gating with audit metadata for overrides/denials.

Wire policy through prompt assembly, tool execution context, and daemon/gateway agent paths; update tests and planning state for Phase 3 PR #2 completion.
This commit is contained in:
William Valentin
2026-02-12 16:06:45 -08:00
parent 125af4e832
commit 90ce622080
18 changed files with 1172 additions and 104 deletions
+26 -7
View File
@@ -2,6 +2,7 @@ import type { ToolResult } from './types.js';
import type { ToolRegistry } from './registry.js';
import type { HookEngine } from '../hooks/engine.js';
import type { ToolPolicyContext } from './policy.js';
import { resolveAutonomy } from '../hooks/autonomy.js';
import { auditLogger } from '../audit/index.js';
export interface ToolExecutorConfig {
@@ -53,31 +54,49 @@ export class ToolExecutor {
}
}
// Check hooks
const action = this.hooks.getAction(toolName);
if (action === 'confirm') {
// Check hooks with autonomy resolution
const baseAction = this.hooks.getAction(toolName);
const autonomyLevel = context?.autonomyLevel ?? 'standard';
const autonomyDecision = resolveAutonomy(toolName, baseAction, autonomyLevel);
const finalAction = autonomyDecision.action;
// Log autonomy override if applicable
if (autonomyDecision.overridden) {
auditLogger?.toolDenied({
tool_name: toolName,
reason: `Autonomy override: ${autonomyDecision.reason}`,
denial_type: 'autonomy_override',
session_id: context?.sessionId,
});
}
if (finalAction === 'confirm') {
const hookResult = await this.hooks.requestConfirmation(
toolName,
args as Record<string, unknown>,
);
if (!hookResult.approved) {
const denyReason = hookResult.reason ?? 'no reason';
const detailedReason = autonomyDecision.overridden
? `${denyReason} (autonomy: ${autonomyDecision.reason})`
: denyReason;
auditLogger?.toolDenied({
tool_name: toolName,
reason: hookResult.reason ?? 'no reason',
reason: detailedReason,
denial_type: 'hook',
session_id: context?.sessionId,
});
return {
success: false,
output: '',
error: `Tool '${toolName}' denied by user: ${hookResult.reason ?? 'no reason'}`,
error: `Tool '${toolName}' denied by user: ${detailedReason}`,
};
}
}
// Execute with timeout
const startTime = Date.now();
auditLogger?.toolStart({
tool_name: toolName,
tool_args: args,
@@ -113,7 +132,7 @@ export class ToolExecutor {
} catch (error) {
const duration = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
auditLogger?.toolError({
tool_name: toolName,
error: errorMessage,