feat: add system disclosure policy gate
Memind CI / Test, build, and release guards (pull_request) Successful in 3m14s
Memind CI / Test, build, and release guards (pull_request) Successful in 3m14s
This commit is contained in:
+64
-1
@@ -283,6 +283,7 @@ export function createAgentRunGateway({
|
||||
tkmindProxy,
|
||||
toolGateway = null,
|
||||
directChatService = null,
|
||||
systemDisclosurePolicyService = null,
|
||||
chatIntentRouter = null,
|
||||
sessionSnapshotService = null,
|
||||
conversationMemoryService = null,
|
||||
@@ -596,6 +597,60 @@ export function createAgentRunGateway({
|
||||
let userMessage = safeJsonParse(row.user_message_json, {});
|
||||
const runOptions = getRunOptionsFromMessage(userMessage);
|
||||
const toolGatewayStatus = toolGateway?.getStatus ? toolGateway.getStatus() : null;
|
||||
let disclosureDecision = null;
|
||||
try {
|
||||
disclosureDecision = systemDisclosurePolicyService?.evaluate?.({
|
||||
userMessage,
|
||||
channel: 'h5',
|
||||
userId: row.user_id,
|
||||
sessionId: row.agent_session_id ?? null,
|
||||
}) ?? null;
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'[AgentRun] system disclosure policy evaluation failed open:',
|
||||
err instanceof Error ? err.message : err,
|
||||
);
|
||||
}
|
||||
if (disclosureDecision?.enforced) {
|
||||
if (!directChatService?.respondDeterministically) {
|
||||
const error = new Error('System Disclosure Policy refusal service unavailable');
|
||||
error.code = 'SYSTEM_DISCLOSURE_REFUSAL_UNAVAILABLE';
|
||||
error.retryable = false;
|
||||
throw error;
|
||||
}
|
||||
const result = await directChatService.respondDeterministically({
|
||||
userId: row.user_id,
|
||||
sessionId: row.agent_session_id ?? null,
|
||||
requestId: row.request_id,
|
||||
userMessage,
|
||||
reply: disclosureDecision.responseText,
|
||||
metadata: {
|
||||
policyId: disclosureDecision.policyId,
|
||||
policyVersion: disclosureDecision.policyVersion,
|
||||
policyReasonCode: disclosureDecision.reasonCode,
|
||||
},
|
||||
onSessionReady: async (activeSessionId) => {
|
||||
await pool.query(
|
||||
`UPDATE h5_agent_runs SET agent_session_id = ?, updated_at = ? WHERE id = ?`,
|
||||
[activeSessionId, nowMs(), runId],
|
||||
);
|
||||
await appendRunSnapshot(runId);
|
||||
},
|
||||
});
|
||||
await appendEvent(runId, 'system_disclosure_blocked', {
|
||||
policyId: disclosureDecision.policyId,
|
||||
policyVersion: disclosureDecision.policyVersion,
|
||||
reasonCode: disclosureDecision.reasonCode,
|
||||
categories: disclosureDecision.categories,
|
||||
channel: 'h5',
|
||||
});
|
||||
return {
|
||||
sessionId: result.sessionId,
|
||||
routing: null,
|
||||
toolEvidence: null,
|
||||
policyBlocked: true,
|
||||
};
|
||||
}
|
||||
const routing = await resolveRunRouting(row, userMessage, runOptions);
|
||||
const routingDecision = resolveLegacyRouteFromClassification(routing) ?? routing?.route ?? null;
|
||||
let agentMemoryContext = null;
|
||||
@@ -867,8 +922,16 @@ export function createAgentRunGateway({
|
||||
runId,
|
||||
row,
|
||||
sessionId,
|
||||
{ routing = null, toolEvidence = null } = {},
|
||||
{ routing = null, toolEvidence = null, policyBlocked = false } = {},
|
||||
) {
|
||||
if (policyBlocked) {
|
||||
await markRun(runId, 'succeeded', {
|
||||
agent_session_id: sessionId,
|
||||
completed_at: nowMs(),
|
||||
error_message: null,
|
||||
}, { expectedStatus: 'running' });
|
||||
return;
|
||||
}
|
||||
assertRequiredImageGenerationCompleted(row, routing, toolEvidence);
|
||||
// `row` was loaded before this worker claimed the run, so its started_at
|
||||
// can still be null. Refresh it before scoping workspace files to the
|
||||
|
||||
Reference in New Issue
Block a user