feat(FN-3561): wire permanent-agent approval context in runtime paths

Wired permanent-agent approval context into runtime paths for FN-3561, updating the agents documentation and adding test coverage for the heartbeat executor to validate the runtime behavior.

Fusion-Task-Id: FN-3561
This commit is contained in:
Fusion
2026-05-07 19:51:02 -07:00
committed by gsxdsm
parent ff6a568f0c
commit a61ea03b13
12 changed files with 329 additions and 17 deletions

View File

@@ -624,13 +624,37 @@ export class HeartbeatMonitor {
};
}
private buildPermanentAgentGatingContext(agent: Agent): { permissionPolicy: ReturnType<typeof resolveEffectiveAgentPermissionPolicy> } | undefined {
private buildPermanentAgentGatingContext(agent: Agent, taskId?: string, runId?: string): import("@fusion/core").PermanentAgentGatingContext | undefined {
if (isEphemeralAgent(agent)) {
return undefined;
}
return {
permissionPolicy: resolveEffectiveAgentPermissionPolicy(agent.permissionPolicy),
requester: { actorId: agent.id, actorType: "agent", actorName: agent.name },
taskId,
runId,
createApprovalRequest: async ({ category, toolName, args }) => this.getApprovalRequestStore().create({
requester: { actorId: agent.id, actorType: "agent", actorName: agent.name },
taskId,
runId,
targetAction: {
category,
action: toolName,
summary: `Permanent-agent gated action for ${toolName}`,
resourceType: "tool",
resourceId: toolName,
context: {
toolName,
toolArgs: args,
source: "permanent-agent-gating",
},
},
}),
findPendingApprovalRequest: async (dedupeKey) => {
const pending = this.getApprovalRequestStore().list({ status: "pending", requesterActorId: agent.id, taskId, limit: 100 });
return pending.find((request) => request.targetAction.context?.approvalDedupeKey === dedupeKey) ?? null;
},
};
}
@@ -1845,7 +1869,7 @@ export class HeartbeatMonitor {
// Skill selection: use waking agent's skills (heartbeat has no role fallback)
...(skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}),
actionGateContext: this.buildActionGateContext(agent, taskId, run.id),
permanentAgentGating: this.buildPermanentAgentGatingContext(agent),
permanentAgentGating: this.buildPermanentAgentGatingContext(agent, taskId, run.id),
});
// Track for monitoring